module SpiController(
  //Host side
  iClk,               //50 MHz
  iStartTransmit,     //Start sending
  iStartAddress,      //Start address
  iEndAddress,        //End address
  oBusy,              //spi controller busy
  //spi transmitter side
  oSclk,
  iMiso,
  oMosi,
  oCs,
  //Memory side
  oMemAddress,
  iMemData,  
  oMemRdClk,
  oMemData,
  oMemWrClk
);

input iClk,iStartTransmit;
input [17:0] iStartAddress;
input [17:0] iEndAddress;
output reg oBusy = 0;
output oSclk;
output oCs;
input iMiso;
output oMosi;
output reg [17:0] oMemAddress = 0;
input [7:0] iMemData;
output reg oMemRdClk = 0;
output reg[7:0] oMemData = 0;
output reg oMemWrClk = 0;

//---------------- Internal registers and wires -----------------------
reg [7:0] spiData = 0;
wire[7:0] spiInput;
reg spiStart = 0;
wire spiBusy;
reg prevStart = 1;
reg [2:0] state = 0;
reg [17:0] endAddr = 0;
reg [3:0] timer = 0;

//---------------- always blocks --------------------------------------

always@(posedge iClk) begin
  prevStart <= iStartTransmit; 
  if(({prevStart,iStartTransmit} == 2'b01) && !oBusy) begin
    oBusy <= 'b1;  
    oMemAddress <= iStartAddress;
    endAddr <= iEndAddress;
    state <= 3'b0;
    oMemRdClk <= 'b0;
    oMemWrClk <= 'b0;
  end
 
  if(oBusy) begin  
    case(state)
      //Set memory address
      0: begin
        oMemRdClk <= 1;
        state <= 'd1;
      end
      
      //Reading spi data
      1: begin
        oMemRdClk <= 0;
        spiData <= iMemData;
        state <= 'd2;
      end
      
      //spi sending start
      2: begin
        spiStart <= 1;
        state <= 'd3;
      end
      
      //Set start to low
      3: begin
        if(spiBusy) begin
          spiStart <= 0;
          state <= 'd4;
        end
      end
      
      //Wait till finished sending
      4: begin
        if(!spiBusy) begin
          state <= 'd5;
          oMemData <= spiInput;          
        end
      end
      
      5: begin
        if(timer == 'b1111) begin
          state <= 'd6;
          oMemWrClk <= 'd1;
          timer <= 'd0;
        end
        else timer <= timer + 1'd1;
      end
      
      //Set next memory address
      6: begin
        oMemAddress <= oMemAddress + 1'b1;
        oMemWrClk <= 'd0;
        state <= 'b0;
        if(oMemAddress >= endAddr) begin
          oBusy <= 0;
        end
      end
    endcase
  end
end

//---------------- sub-modules --------------------------------------
SpiTransmitter trans(
  .iClk(~iClk),
  .oSclk(oSclk),
  .iMiso(iMiso),
  .oMosi(oMosi),
  .oCs(oCs),
  .iData(spiData),
  .oData(spiInput),
  .iStart(spiStart),
  .oBusy(spiBusy)
);

endmodule
